#include "reactor/tcp_connection.hpp"
#include "reactor/event_loop.hpp"
#include "reactor/channel.hpp"
#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cerrno>
#include <sys/socket.h>

namespace reactor {

TcpConnection::TcpConnection(EventLoop* loop, int sockfd, const struct sockaddr_in& localAddr, const struct sockaddr_in& peerAddr)
    : _loop(loop),
      _sockfd(sockfd),
      _state(kConnecting),
      _channel(new Channel(loop, sockfd)),
      _localAddr(localAddr),
      _peerAddr(peerAddr)
{
    _channel->setReadCallback(std::bind(&TcpConnection::handleRead, this));
    _channel->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    _channel->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
    _channel->setErrorCallback(std::bind(&TcpConnection::handleError, this));
}

TcpConnection::~TcpConnection() {
    assert(_state == kDisconnected);
}

void TcpConnection::send(const std::string& message) {
    if (_state == kConnected) {
        if (_loop->isInLoopThread()) {
            sendInLoop(message);
        } else {
            // 使用 string 构造函数进行拷贝，确保 message 生命周期
            _loop->queueInLoop(std::bind(&TcpConnection::sendInLoop, shared_from_this(), std::string(message)));
        }
    }
}

void TcpConnection::sendInLoop(const std::string& message) {
    _loop->assertInLoopThread();
    ssize_t nwrote = 0;
    size_t remaining = message.size();
    bool faultError = false;

    if (!_channel->isWriting() && _outputBuffer.readableBytes() == 0) {
        nwrote = ::write(_sockfd, message.data(), message.size());
        if (nwrote >= 0) {
            remaining = message.size() - nwrote;
            if (remaining == 0) {
                // Sent completely
            }
        } else {
            nwrote = 0;
            if (errno != EWOULDBLOCK) {
                std::cerr << "TcpConnection::sendInLoop write error" << std::endl;
                faultError = true;
            }
        }
    }

    if (!faultError && remaining > 0) {
        _outputBuffer.append(message.data() + nwrote, remaining);
        if (!_channel->isWriting()) {
            _channel->enableWriting();
        }
    }
}

void TcpConnection::shutdown() {
    if (_state == kConnected) {
        setState(kDisconnecting);
        _loop->runInLoop(std::bind(&TcpConnection::shutdownInLoop, shared_from_this()));
    }
}

void TcpConnection::shutdownInLoop() {
    _loop->assertInLoopThread();
    if (!_channel->isWriting()) {
        ::shutdown(_sockfd, SHUT_WR);
    }
}

void TcpConnection::connectEstablished() {
    _loop->assertInLoopThread();
    assert(_state == kConnecting);
    setState(kConnected);
    _channel->enableReading();
    if (_connectionCallback) {
        _connectionCallback(shared_from_this());
    }
}

void TcpConnection::connectDestroyed() {
    _loop->assertInLoopThread();
    if (_state == kConnected) {
        setState(kDisconnected);
        _channel->disableAll();
        if (_connectionCallback) {
            _connectionCallback(shared_from_this());
        }
    }
    _channel->remove();
}

void TcpConnection::handleRead() {
    _loop->assertInLoopThread();
    int savedErrno = 0;
    ssize_t n = _inputBuffer.readFd(_sockfd, &savedErrno);
    if (n > 0) {
        _messageCallback(shared_from_this(), &_inputBuffer);
    } else if (n == 0) {
        handleClose();
    } else {
        errno = savedErrno;
        std::cerr << "TcpConnection::handleRead error" << std::endl;
        handleError();
    }
}

void TcpConnection::handleWrite() {
    _loop->assertInLoopThread();
    if (_channel->isWriting()) {
        ssize_t n = ::write(_sockfd, _outputBuffer.peek(), _outputBuffer.readableBytes());
        if (n > 0) {
            _outputBuffer.retrieve(n);
            if (_outputBuffer.readableBytes() == 0) {
                _channel->disableWriting();
                if (_state == kDisconnecting) {
                    shutdownInLoop();
                }
            }
        } else {
            std::cerr << "TcpConnection::handleWrite error" << std::endl;
        }
    }
}

void TcpConnection::handleClose() {
    _loop->assertInLoopThread();
    setState(kDisconnected);
    _channel->disableAll();
    TcpConnectionPtr guardThis(shared_from_this());
    if (_connectionCallback) {
        _connectionCallback(guardThis);
    }
    if (_closeCallback) {
        _closeCallback(guardThis);
    }
}

void TcpConnection::handleError() {
    int err;
    socklen_t len = sizeof(err);
    if (::getsockopt(_sockfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
        err = errno;
    }
    std::cerr << "TcpConnection::handleError, SO_ERROR = " << err << std::endl;
}

} // namespace reactor
